#!/bin/bash

# Usage:
#   ./gen-pipeline.sh gen-scope-image <source_cnf_file> <origin|sorted|all> <output_dir> <feature_output_dir> [--track=trackname] [--feature_dim=N]
#   ./gen-pipeline.sh gen-ascii-image <source_cnf_file> <min|max|mean|direct> <output_dir> <feature_output_dir> [--track=trackname] [--feature_dim=N]
#   ./gen-pipeline.sh batch gen-scope-image <input_dir> <output_dir> <feature_output_dir> [--track=trackname] [--jobs=N] [--load=XX%] [--feature_dim=N] [--memfree=XG]
#   ./gen-pipeline.sh batch gen-ascii-image <input_dir> <output_dir> <feature_output_dir> [--track=trackname] [--jobs=N] [--load=XX%] [--feature_dim=N] [--memfree=XG]

set -e

# Function to create log directory and get log file path
get_log_file() {
    local out_dir="$1"
    local hash="$2"
    local family="$3"
    local type="$4"
    local log_dir="$out_dir/logs"
    mkdir -p "$log_dir"
    echo "$log_dir/${hash}-${family}.${type}.log"
}

# Function to get family from database
get_family_from_db() {
    local hash="$1"
    local track="$2"
    local db="meta.db"
    
    if [ ! -f "$db" ]; then
        echo "Error: meta.db not found in current directory."
        return 1
    fi
    
    # Only use track in query if it's not a number (feature_dim)
    if [ -n "$track" ] && ! [[ "$track" =~ ^[0-9]+$ ]]; then
        sql="SELECT family FROM features f LEFT JOIN track t ON f.hash = t.hash WHERE f.hash = '$hash' AND t.value LIKE '%$track%' LIMIT 1;"
    else
        sql="SELECT family FROM features WHERE hash = '$hash' LIMIT 1;"
    fi
    
    family=$(sqlite3 "$db" "$sql")
    if [ -z "$family" ]; then
        return 1
    fi
    echo "$family"
}

# Function to find satlab binary
find_satlab_bin() {
    local satlab_bin="cpp/satlab"
    if [ ! -x "$satlab_bin" ]; then
        satlab_bin="satlab"
    fi
    if ! command -v "$satlab_bin" >/dev/null 2>&1; then
        echo "Error: satlab binary not found."
        return 1
    fi
    echo "$satlab_bin"
}

if [ "$1" == "batch" ]; then
    shift
    if [ $# -lt 5 ]; then
        echo "Usage: $0 batch gen-scope-image <input_dir> <image_output_dir> <feature_output_dir> [--track=trackname] [--jobs=N] [--load=XX%] [--feature_dim=N]"
        echo "   or: $0 batch gen-ascii-image <input_dir> <image_output_dir> <feature_output_dir> [--track=trackname] [--jobs=N] [--load=XX%] [--feature_dim=N]"
        exit 1
    fi
    MODE="$1"
    INPUT_DIR="$2"
    IMAGE_OUT_DIR="$3"
    FEATURE_OUT_DIR="$4"

    shift 4
    TRACK=""
    JOBS="24"
    LOAD="90%"
    FEATURE_DIM="128"
    MEMFREE="8G"
    
    # Parse remaining optional arguments
    while [ $# -gt 0 ]; do
        case "$1" in
            --track=*) TRACK="${1#--track=}" ;;
            --jobs=*) JOBS="${1#--jobs=}" ;;
            --load=*) LOAD="${1#--load=}" ;;
            --feature_dim=*) FEATURE_DIM="${1#--feature_dim=}" ;;
            --memfree=*) MEMFREE="${1#--memfree=}" ;;
            *) echo "Unknown option: $1"; exit 1 ;;
        esac
        shift
    done
    
    # Export variables for parallel processes
    export FEATURE_DIM
    export TRACK
    
    # Check if all required directories exist
    if [ ! -d "$INPUT_DIR" ]; then
        echo "Error: Input directory '$INPUT_DIR' does not exist."
        exit 2
    fi
    
    if [ ! -d "$IMAGE_OUT_DIR" ]; then
        echo "Error: Image output directory '$IMAGE_OUT_DIR' does not exist."
        exit 2
    fi
    
    if [ ! -d "$FEATURE_OUT_DIR" ]; then
        echo "Error: Feature output directory '$FEATURE_OUT_DIR' does not exist."
        exit 2
    fi
    
    # Find all .cnf and .cnf.xz files in the input directory
    find "$INPUT_DIR" -type f \( -name "*.cnf" -o -name "*.cnf.xz" \) > filelist.txt
    NUM_FILES=$(cat filelist.txt | wc -l)
    echo "[INFO] Found $NUM_FILES CNF files to process."
    
    if [ -z "$IMAGE_OUT_DIR" ]; then
        echo "Error: IMAGE_OUT_DIR is not set or empty."
        exit 1
    fi
    if [ -z "$FEATURE_OUT_DIR" ]; then
        echo "Error: FEATURE_OUT_DIR is not set or empty."
        exit 1
    fi
    
    if [ "$MODE" == "gen-scope-image" ]; then


        # Create a helper function for parallel processing scope images
        process_scope_file() {
            local cnf_file="$1"
            local image_out_dir="$2"
            local feature_out_dir="$3"
            local track="$4"
            local feature_dim="128"
            
            local basename=$(basename "$cnf_file")
            local hash=$(echo "$basename" | cut -d'-' -f1)
            
            # Get family from database
            family=$(get_family_from_db "$hash" "$track")
            if [ $? -ne 0 ]; then
                if [ -n "$track" ]; then
                    echo "SKIP:$cnf_file (hash $hash not found in database with track '$track')"
                else
                    echo "SKIP:$cnf_file (hash $hash not found in database)"
                fi
                return 0
            fi
            
            local origin_file="$image_out_dir/${hash}-${family}.origin.bmp"
            local sorted_file="$image_out_dir/${hash}-${family}.sorted.bmp"
            local feature_file="$feature_out_dir/${hash}-${family}-scope.npz"
            local log_file="$image_out_dir/logs/${hash}-${family}.scope.log"
            local feature_log_file="$feature_out_dir/logs/${hash}-${family}.scope.log"
            
            # Check if log directories exist
            if [ ! -d "$(dirname "$log_file")" ] || [ ! -d "$feature_out_dir/logs" ]; then
                echo "FAIL:$cnf_file (log directories do not exist)"
                return 1
            fi
            
            # Check if feature file already exists
            if [ -f "$feature_file" ]; then
                echo "SKIP:$cnf_file (feature file already exists)"
                return 0
            fi
            
            # Check if both images exist
            origin_exists=false
            sorted_exists=false
            
            if [ -f "$origin_file" ]; then
                origin_exists=true
            fi
            
            if [ -f "$sorted_file" ]; then
                sorted_exists=true
            fi
            
            # Generate missing images
            satlab_bin=$(find_satlab_bin)
            if [ $? -ne 0 ]; then
                echo "FAIL:$cnf_file (satlab binary not found)"
                return 1
            fi
            
            if [ "$origin_exists" = false ]; then
                echo "$hash" > "$log_file"
                echo "Generating origin image..." >> "$log_file"
                if ! "$satlab_bin" gen-scope-image "$cnf_file" "origin" "$origin_file" 2>&1 | tee -a "$log_file"; then
                    echo "FAIL:$cnf_file (failed to generate origin image)"
                    return 1
                fi
            fi
            
            if [ "$sorted_exists" = false ]; then
                echo "Generating sorted image..." >> "$log_file"
                if ! "$satlab_bin" gen-scope-image "$cnf_file" "sorted" "$sorted_file" 2>&1 | tee -a "$log_file"; then
                    echo "FAIL:$cnf_file (failed to generate sorted image)"
                    return 1
                fi
            fi
            
            # Validate feature_dim
            if [ -z "$feature_dim" ] || ! [[ "$feature_dim" =~ ^[0-9]+$ ]]; then
                echo "FAIL:$cnf_file (invalid feature dimension: '$feature_dim')"
                return 1
            fi
            
            # Generate features
            echo "Generating features..." >> "$log_file"
            echo "Using feature dimension: $feature_dim" >> "$log_file"
            if ! python3 feature_extraction.py scope \
                --origin_image "$origin_file" \
                --sorted_image "$sorted_file" \
                --output_file "$feature_file" \
                --uuid "$hash" \
                --feature_dim "$feature_dim" 2> >(tee -a "$feature_log_file" >&2); then
                error_msg=$(tail -n 1 "$feature_log_file")
                echo "FAIL:$cnf_file (feature extraction failed: $error_msg)"
                return 1
            fi
            echo "DONE:$cnf_file"
        }
        export -f process_scope_file get_family_from_db find_satlab_bin
        
        # Use parallel to process files
        TOTAL_FILES=$(cat filelist.txt | wc -l)
        echo "[INFO] Processing $TOTAL_FILES scope files with feature dimension $FEATURE_DIM..."
        
        export FEATURE_DIM
        cat filelist.txt | parallel --env FEATURE_DIM --bar --jobs "$JOBS" --load "$LOAD" --memfree "$MEMFREE" process_scope_file {} "$IMAGE_OUT_DIR" "$FEATURE_OUT_DIR" "$TRACK" "$FEATURE_DIM"
        
        # Count results
        DONE_COUNT=$(grep "^DONE:" parallel.log 2>/dev/null | wc -l || echo "0")
        SKIP_COUNT=$(grep "^SKIP:" parallel.log 2>/dev/null | wc -l || echo "0")
        FAIL_COUNT=$(grep "^FAIL:" parallel.log 2>/dev/null | wc -l || echo "0")
        
        echo "[INFO] Processing complete: $DONE_COUNT done, $SKIP_COUNT skipped, $FAIL_COUNT failed"
        
    elif [ "$MODE" == "gen-ascii-image" ]; then
        # Create a helper function for parallel processing ascii images
        process_ascii_file() {
            local cnf_file="$1"
            local image_out_dir="$2"
            local feature_out_dir="$3"
            local track="$4"
            local feature_dim="$5"
            
            local basename=$(basename "$cnf_file")
            local hash=$(echo "$basename" | cut -d'-' -f1)
            
            # Get family from database
            family=$(get_family_from_db "$hash" "$track")
            if [ $? -ne 0 ]; then
                if [ -n "$track" ]; then
                    echo "SKIP:$cnf_file (hash $hash not found in database with track '$track')"
                else
                    echo "SKIP:$cnf_file (hash $hash not found in database)"
                fi
                return 0
            fi
            
            local direct_file="$image_out_dir/${hash}~${family}~ascii~direct~512.bmp"
            local min_file="$image_out_dir/${hash}~${family}~ascii~min~512.bmp"
            local max_file="$image_out_dir/${hash}~${family}~ascii~max~512.bmp"
            local mean_file="$image_out_dir/${hash}~${family}~ascii~mean~512.bmp"
            local feature_file="$feature_out_dir/${hash}~${family}~ascii.npz"
            local log_file="$image_out_dir/logs/${hash}-${family}.ascii.log"
            local feature_log_file="$feature_out_dir/logs/${hash}-${family}.ascii.log"
            
            # Check if log directories exist
            if [ ! -d "$(dirname "$log_file")" ] || [ ! -d "$feature_out_dir/logs" ]; then
                echo "FAIL:$cnf_file (log directories do not exist)"
                return 1
            fi
            
            # Check if feature file already exists
            if [ -f "$feature_file" ]; then
                echo "SKIP:$cnf_file (feature file already exists)"
                return 0
            fi
            
            # Check which images exist
            direct_exists=false
            min_exists=false
            max_exists=false
            mean_exists=false
            
            if [ -f "$direct_file" ]; then
                direct_exists=true
            fi
            
            if [ -f "$min_file" ]; then
                min_exists=true
            fi
            
            if [ -f "$max_file" ]; then
                max_exists=true
            fi
            
            if [ -f "$mean_file" ]; then
                mean_exists=true
            fi
            
            # Generate missing images
            satlab_bin=$(find_satlab_bin)
            if [ $? -ne 0 ]; then
                echo "FAIL:$cnf_file (satlab binary not found)"
                return 1
            fi
            
            echo "$hash" > "$log_file"
            
            if [ "$direct_exists" = false ]; then
                echo "Generating direct image..." >> "$log_file"
                if ! "$satlab_bin" gen-ascii-image "$cnf_file" "direct" "$direct_file" 2>&1 | tee -a "$log_file"; then
                    echo "FAIL:$cnf_file (failed to generate direct image)"
                    return 1
                fi
            fi
            
            if [ "$min_exists" = false ]; then
                echo "Generating min image..." >> "$log_file"
                if ! "$satlab_bin" gen-ascii-image "$cnf_file" "min" "$min_file" 2>&1 | tee -a "$log_file"; then
                    echo "FAIL:$cnf_file (failed to generate min image)"
                    return 1
                fi
            fi
            
            if [ "$max_exists" = false ]; then
                echo "Generating max image..." >> "$log_file"
                if ! "$satlab_bin" gen-ascii-image "$cnf_file" "max" "$max_file" 2>&1 | tee -a "$log_file"; then
                    echo "FAIL:$cnf_file (failed to generate max image)"
                    return 1
                fi
            fi
            
            if [ "$mean_exists" = false ]; then
                echo "Generating mean image..." >> "$log_file"
                if ! "$satlab_bin" gen-ascii-image "$cnf_file" "mean" "$mean_file" 2>&1 | tee -a "$log_file"; then
                    echo "FAIL:$cnf_file (failed to generate mean image)"
                    return 1
                fi
            fi
            
            # Validate feature_dim
            if [ -z "$feature_dim" ] || ! [[ "$feature_dim" =~ ^[0-9]+$ ]]; then
                echo "FAIL:$cnf_file (invalid feature dimension: '$feature_dim')"
                return 1
            fi
            
            # Generate features
            echo "Generating features..." >> "$log_file"
            echo "Using feature dimension: $feature_dim" >> "$log_file"
            if ! python3 feature_extraction.py ascii \
                --direct_image "$direct_file" \
                --min_image "$min_file" \
                --max_image "$max_file" \
                --mean_image "$mean_file" \
                --output_file "$feature_file" \
                --uuid "$hash" \
                --feature_dim "$feature_dim" 2> >(tee -a "$feature_log_file" >&2); then
                error_msg=$(tail -n 1 "$feature_log_file")
                echo "FAIL:$cnf_file (feature extraction failed: $error_msg)"
                return 1
            fi
            echo "DONE:$cnf_file"
        }
        export -f process_ascii_file get_family_from_db find_satlab_bin
        
        # Use parallel to process files
        TOTAL_FILES=$(cat filelist.txt | wc -l)
        echo "[INFO] Processing $TOTAL_FILES ascii files with feature dimension $FEATURE_DIM..."
        
        export FEATURE_DIM
        cat filelist.txt | parallel --env FEATURE_DIM --bar --jobs "$JOBS" --load "$LOAD" --memfree "$MEMFREE" process_ascii_file {} "$IMAGE_OUT_DIR" "$FEATURE_OUT_DIR" "$TRACK" "$FEATURE_DIM"
        
        # Count results
        DONE_COUNT=$(grep "^DONE:" parallel.log 2>/dev/null | wc -l || echo "0")
        SKIP_COUNT=$(grep "^SKIP:" parallel.log 2>/dev/null | wc -l || echo "0")
        FAIL_COUNT=$(grep "^FAIL:" parallel.log 2>/dev/null | wc -l || echo "0")
        
        echo "[INFO] Processing complete: $DONE_COUNT done, $SKIP_COUNT skipped, $FAIL_COUNT failed"
        
    else
        echo "Error: Unknown mode '$MODE'. Use 'gen-scope-image' or 'gen-ascii-image'."
        exit 1
    fi
    
    rm -f filelist.txt parallel.log 2>/dev/null
    echo "[INFO] Batch processing complete."
    exit 0
fi

    if [ $# -lt 5 ]; then
        echo "Usage: $0 gen-scope-image <source_cnf_file> <origin|sorted|all> <output_dir> <feature_output_dir> [--track=trackname] [--feature_dim=N]"
        echo "   or: $0 gen-ascii-image <source_cnf_file> <min|max|mean|direct> <output_dir> <feature_output_dir> [--track=trackname] [--feature_dim=N]"
        echo "   or: $0 batch ... (see above)"
        exit 1
    fi

MODE="$1"
shift

if [ "$MODE" == "gen-ascii-image" ]; then
    SRC_FILE="$1"
    TYPE="$2"
    IMAGE_OUT_DIR="$3"
    FEATURE_OUT_DIR="$4"
    TRACK=""
    FEATURE_DIM="128"
    if [ $# -ge 5 ]; then
        for arg in "$@"; do
            case "$arg" in
                --track=*) TRACK="${arg#--track=}" ;;
                --feature_dim=*) FEATURE_DIM="${arg#--feature_dim=}" ;;
            esac
        done
    fi
    if [ ! -f "$SRC_FILE" ]; then
        echo "Error: Source file '$SRC_FILE' does not exist."
        exit 2
    fi
    if [[ "$TYPE" != "min" && "$TYPE" != "max" && "$TYPE" != "mean" && "$TYPE" != "direct" ]]; then
        echo "Error: Type must be one of min, max, mean, direct."
        exit 3
    fi
    mkdir -p "$IMAGE_OUT_DIR"
    mkdir -p "$FEATURE_OUT_DIR"
    if [ -z "$IMAGE_OUT_DIR" ]; then
        echo "Error: IMAGE_OUT_DIR is not set or empty."
        exit 1
    fi
    if [ -z "$FEATURE_OUT_DIR" ]; then
        echo "Error: FEATURE_OUT_DIR is not set or empty."
        exit 1
    fi
    BASENAME=$(basename "$SRC_FILE")
    HASH=$(echo "$BASENAME" | cut -d'-' -f1)
    FAMILY=$(get_family_from_db "$HASH" "$TRACK")
    if [ $? -ne 0 ]; then
        if [ -n "$TRACK" ]; then
            echo "[SKIP] $SRC_FILE: hash $HASH not found in database with track '$TRACK'"
        else
            echo "[SKIP] $SRC_FILE: hash $HASH not found in database"
        fi
        exit 0
    fi
    OUT_FILE="$IMAGE_OUT_DIR/${HASH}~${FAMILY}~ascii~${TYPE}~512.bmp"
    FEATURE_FILE="$FEATURE_OUT_DIR/${HASH}~${FAMILY}~ascii.npz"
    LOG_FILE=$(get_log_file "$IMAGE_OUT_DIR" "$HASH" "$FAMILY" "$TYPE")
    FEATURE_LOG_FILE="$FEATURE_OUT_DIR/logs/${HASH}~${FAMILY}~ascii.log"
    mkdir -p "$FEATURE_OUT_DIR/logs"
    SATLAB_BIN=$(find_satlab_bin)
    if [ $? -ne 0 ]; then
        exit 5
    fi
    
    # Record hash ID and run satlab with logging
    echo "$HASH" > "$LOG_FILE"
    if "$SATLAB_BIN" gen-ascii-image "$SRC_FILE" "$TYPE" "$OUT_FILE" 2>&1 | tee -a "$LOG_FILE"; then
        if [ -f "$OUT_FILE" ]; then
            # Generate features
            if python3 feature_extraction.py ascii \
                --${TYPE}_image "$OUT_FILE" \
                --output_file "$FEATURE_FILE" \
                --uuid "$HASH" \
                --feature_dim "$FEATURE_DIM" >> "$FEATURE_LOG_FILE" 2>&1; then
                echo "[DONE] $OUT_FILE (log: $LOG_FILE, feature log: $FEATURE_LOG_FILE)"
                exit 0
            else
                echo "[FAIL] $OUT_FILE (feature extraction failed, log: $FEATURE_LOG_FILE)"
                exit 6
            fi
        else
            echo "[FAIL] $OUT_FILE (log: $LOG_FILE)"
            exit 6
        fi
    else
        echo "[FAIL] $OUT_FILE (log: $LOG_FILE)"
        exit 6
    fi
elif [ "$MODE" == "gen-scope-image" ]; then
    SRC_FILE="$1"
    TYPE="$2"
    IMAGE_OUT_DIR="$3"
    FEATURE_OUT_DIR="$4"
    TRACK=""
    FEATURE_DIM="128"
    if [ $# -ge 5 ]; then
        for arg in "$@"; do
            case "$arg" in
                --track=*) TRACK="${arg#--track=}" ;;
                --feature_dim=*) FEATURE_DIM="${arg#--feature_dim=}" ;;
            esac
        done
    fi
    if [ ! -f "$SRC_FILE" ]; then
        echo "Error: Source file '$SRC_FILE' does not exist."
        exit 2
    fi
    if [ "$TYPE" != "origin" ] && [ "$TYPE" != "sorted" ] && [ "$TYPE" != "all" ]; then
        echo "Error: Type must be 'origin', 'sorted', or 'all'."
        exit 3
    fi
    mkdir -p "$IMAGE_OUT_DIR"
    mkdir -p "$FEATURE_OUT_DIR"
    if [ -z "$IMAGE_OUT_DIR" ]; then
        echo "Error: IMAGE_OUT_DIR is not set or empty."
        exit 1
    fi
    if [ -z "$FEATURE_OUT_DIR" ]; then
        echo "Error: FEATURE_OUT_DIR is not set or empty."
        exit 1
    fi
    BASENAME=$(basename "$SRC_FILE")
    HASH=$(echo "$BASENAME" | cut -d'-' -f1)
    FAMILY=$(get_family_from_db "$HASH" "$TRACK")
    if [ $? -ne 0 ]; then
        if [ -n "$TRACK" ]; then
            echo "[SKIP] $SRC_FILE: hash $HASH not found in database with track '$TRACK'"
        else
            echo "[SKIP] $SRC_FILE: hash $HASH not found in database"
        fi
        exit 0
    fi
    SATLAB_BIN=$(find_satlab_bin)
    if [ $? -ne 0 ]; then
        exit 5
    fi
    if [ "$TYPE" == "all" ]; then
        OUT_FILE_ORIGIN="$IMAGE_OUT_DIR/${HASH}-${FAMILY}.origin.bmp"
        OUT_FILE_SORTED="$IMAGE_OUT_DIR/${HASH}-${FAMILY}.sorted.bmp"
        FEATURE_FILE="$FEATURE_OUT_DIR/${HASH}-${FAMILY}-scope.npz"
        LOG_FILE_ORIGIN=$(get_log_file "$IMAGE_OUT_DIR" "$HASH" "$FAMILY" "origin")
        LOG_FILE_SORTED=$(get_log_file "$IMAGE_OUT_DIR" "$HASH" "$FAMILY" "sorted")
        FEATURE_LOG_FILE="$FEATURE_OUT_DIR/logs/${HASH}-${FAMILY}-scope.log"
        mkdir -p "$FEATURE_OUT_DIR/logs"
        # Process origin
        echo "$HASH" > "$LOG_FILE_ORIGIN"
        if "$SATLAB_BIN" gen-scope-image "$SRC_FILE" "origin" "$OUT_FILE_ORIGIN" 2>&1 | tee -a "$LOG_FILE_ORIGIN"; then
            RET1=0
        else
            RET1=1
        fi
        # Process sorted
        echo "$HASH" > "$LOG_FILE_SORTED"
        if "$SATLAB_BIN" gen-scope-image "$SRC_FILE" "sorted" "$OUT_FILE_SORTED" 2>&1 | tee -a "$LOG_FILE_SORTED"; then
            RET2=0
        else
            RET2=1
        fi
        if [ $RET1 -eq 0 ] && [ -f "$OUT_FILE_ORIGIN" ] && [ $RET2 -eq 0 ] && [ -f "$OUT_FILE_SORTED" ]; then
            # Generate features
            if python3 feature_extraction.py scope \
                --origin_image "$OUT_FILE_ORIGIN" \
                --sorted_image "$OUT_FILE_SORTED" \
                --output_file "$FEATURE_FILE" \
                --uuid "$HASH" \
                --feature_dim "$FEATURE_DIM" >> "$FEATURE_LOG_FILE" 2>&1; then
                echo "[DONE] $OUT_FILE_ORIGIN (log: $LOG_FILE_ORIGIN, feature log: $FEATURE_LOG_FILE)"
                echo "[DONE] $OUT_FILE_SORTED (log: $LOG_FILE_SORTED, feature log: $FEATURE_LOG_FILE)"
                exit 0
            else
                echo "[FAIL] $OUT_FILE_ORIGIN (feature extraction failed, log: $FEATURE_LOG_FILE)"
                exit 6
            fi
        else
            echo "[FAIL] Some files failed to generate"
            exit 6
        fi
    else
        OUT_FILE="$IMAGE_OUT_DIR/${HASH}-${FAMILY}.${TYPE}.bmp"
        FEATURE_FILE="$FEATURE_OUT_DIR/${HASH}-${FAMILY}-scope.npz"
        LOG_FILE=$(get_log_file "$IMAGE_OUT_DIR" "$HASH" "$FAMILY" "$TYPE")
        FEATURE_LOG_FILE="$FEATURE_OUT_DIR/logs/${HASH}-${FAMILY}-scope.log"
        mkdir -p "$FEATURE_OUT_DIR/logs"
        # Record hash ID and run satlab with logging
        echo "$HASH" > "$LOG_FILE"
        if "$SATLAB_BIN" gen-scope-image "$SRC_FILE" "$TYPE" "$OUT_FILE" 2>&1 | tee -a "$LOG_FILE"; then
            if [ -f "$OUT_FILE" ]; then
                # Generate features
                if python3 feature_extraction.py scope \
                    --origin_image "$OUT_FILE" \
                    --sorted_image "$OUT_FILE" \
                    --output_file "$FEATURE_FILE" \
                    --uuid "$HASH" \
                    --feature_dim "$FEATURE_DIM" >> "$FEATURE_LOG_FILE" 2>&1; then
                    echo "[DONE] $OUT_FILE (log: $LOG_FILE, feature log: $FEATURE_LOG_FILE)"
                    exit 0
                else
                    echo "[FAIL] $OUT_FILE (feature extraction failed, log: $FEATURE_LOG_FILE)"
                    exit 6
                fi
            else
                echo "[FAIL] $OUT_FILE (log: $LOG_FILE)"
                exit 6
            fi
        else
            echo "[FAIL] $OUT_FILE (log: $LOG_FILE)"
            exit 6
        fi
    fi
else
    echo "Usage: $0 gen-scope-image <source_cnf_file> <origin|sorted|all> <output_dir> <feature_output_dir> [--track=trackname]"
    echo "   or: $0 gen-ascii-image <source_cnf_file> <min|max|mean|direct> <output_dir> <feature_output_dir> [--track=trackname]"
    echo "   or: $0 batch ... (see above)"
    exit 1
fi 